﻿using System;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Xml.Serialization;

namespace DocBot.Util
{
	/*
	The below example code is offered "as-is" with no warrantees of any kind. Use at your own risk.
	*/
	// http://www.bengribaudo.com/blog/2012/03/14/1942/saving-restoring-wpf-datagrid-columns-size-sorting-and-order
	/// <summary>
	/// Saves ColumnOrder during the Session
	/// </summary>
	public class EnhancedDataGrid : DataGrid
	{
		private bool inWidthChange = false;
		private bool updatingColumnInfo = false;

		public static readonly DependencyProperty ColumnInfoProperty = DependencyProperty.Register("ColumnInfo",
				typeof(ObservableCollection<ColumnInfo>), typeof(EnhancedDataGrid),
				new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, ColumnInfoChangedCallback)
			);

		private static void ColumnInfoChangedCallback(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e)
		{
			var grid = (EnhancedDataGrid)dependencyObject;
			if (!grid.updatingColumnInfo) { grid.ColumnInfoChanged(); }
		}

		protected override void OnInitialized(EventArgs e)
		{
			EventHandler sortDirectionChangedHandler = (sender, x) => UpdateColumnInfo();
			EventHandler widthPropertyChangedHandler = (sender, x) => inWidthChange = true;
			var sortDirectionPropertyDescriptor = DependencyPropertyDescriptor.FromProperty(DataGridColumn.SortDirectionProperty, typeof(DataGridColumn));
			var widthPropertyDescriptor = DependencyPropertyDescriptor.FromProperty(DataGridColumn.WidthProperty, typeof(DataGridColumn));

			Loaded += (sender, x) =>
			{
				foreach (var column in Columns)
				{
					sortDirectionPropertyDescriptor.AddValueChanged(column, sortDirectionChangedHandler);
					widthPropertyDescriptor.AddValueChanged(column, widthPropertyChangedHandler);
				}
			};
			Unloaded += (sender, x) =>
			{
				foreach (var column in Columns)
				{
					sortDirectionPropertyDescriptor.RemoveValueChanged(column, sortDirectionChangedHandler);
					widthPropertyDescriptor.RemoveValueChanged(column, widthPropertyChangedHandler);
				}
			};

			base.OnInitialized(e);
		}

		public ObservableCollection<ColumnInfo> ColumnInfo
		{
			get { return (ObservableCollection<ColumnInfo>)GetValue(ColumnInfoProperty); }
			set { SetValue(ColumnInfoProperty, value); }
		}

		private void UpdateColumnInfo()
		{
			updatingColumnInfo = true;
			ColumnInfo = new ObservableCollection<ColumnInfo>(Columns.Select((x) => new ColumnInfo(x)));
			updatingColumnInfo = false;
		}

		protected override void OnColumnReordered(DataGridColumnEventArgs e)
		{
			UpdateColumnInfo();
			base.OnColumnReordered(e);
		}

		protected override void OnPreviewMouseLeftButtonUp(System.Windows.Input.MouseButtonEventArgs e)
		{
			if (inWidthChange)
			{
				inWidthChange = false;
				UpdateColumnInfo();
			}
			base.OnPreviewMouseLeftButtonUp(e);
		}

		private void ColumnInfoChanged()
		{
			Items.SortDescriptions.Clear();
			foreach (var column in ColumnInfo)
			{
				var realColumn = Columns.Where((x) => column.Header.Equals(x.Header)).FirstOrDefault();
				if (realColumn == null) { continue; }
				column.Apply(realColumn, Columns.Count, Items.SortDescriptions);
			}
		}
	}

	public struct ColumnInfo
	{
		#region Properties
		private object _header;

		public object Header
		{
			get { return _header; }
			set { _header = value; }
		}
		private string _propertyPath;

		public string PropertyPath
		{
			get { return _propertyPath; }
			set { _propertyPath = value; }
		}

		[XmlIgnore]
		public ListSortDirection? SortDirection;

		private int _displayIndex;

		public int DisplayIndex
		{
			get { return _displayIndex; }
			set { _displayIndex = value; }
		}

		private double _widthValue;

		public double WidthValue
		{
			get { return _widthValue; }
			set { _widthValue = value; }
		}

		[XmlIgnore]
		public DataGridLengthUnitType WidthType;

		#endregion

		public ColumnInfo(DataGridColumn column)
		{
			_header = column.Header;
			if (!(column is DataGridComboBoxColumn))
				_propertyPath = ((Binding)((DataGridBoundColumn)column).Binding).Path.Path;
			else
				_propertyPath = column.SortMemberPath;
			//PropertyPath = column.SortMemberPath;
			_widthValue = column.Width.DisplayValue;
			WidthType = column.Width.UnitType;
			SortDirection = column.SortDirection;
			_displayIndex = column.DisplayIndex;
		}

		public void Apply(DataGridColumn column, int gridColumnCount, SortDescriptionCollection sortDescriptions)
		{
			column.Width = new DataGridLength(_widthValue, WidthType);
			column.SortDirection = SortDirection;
			if (SortDirection != null)
			{
				sortDescriptions.Add(new SortDescription(_propertyPath, SortDirection.Value));
			}
			if (column.DisplayIndex != _displayIndex)
			{
				var maxIndex = (gridColumnCount == 0) ? 0 : gridColumnCount - 1;
				column.DisplayIndex = (_displayIndex <= maxIndex) ? _displayIndex : maxIndex;
			}
		}
	}
}

